import Rte from 'editor/index'
import { MouseBlock, MouseElement, MousePath, MouseType, MouseZone, ZONE } from './declare'

/**
 * 可以进行验证的 MouseEvent.type 类型
 */
const MOUSETYPE = ['mousedown', 'mouseup']

export default class RteMouse {
  public constructor(public editor: Rte) {
    const { editable } = editor.$refs

    // 鼠标按下状态下的 editable mouseenter 事件
    const mouseenter = () => {
      this.mousepointer = ZONE.EDITABLE
    }

    // 鼠标按下状态下的 editable mouseleave 事件
    const mouseleave = () => {
      editor.selection.clear()
      this.mousepointer = ZONE.UNVALID
    }

    // 鼠标按下事件的回调函数
    const mousedown = (e: MouseEvent) => {
      editor.input.blur()

      this.transform(e)

      // if (getActive(editor) !== this.mouseelement.mousedown) {
      //   removeActive(editor)
      // }

      // 绑定选区的 selectionchange 监听
      if (this.mousezone.mousedown !== ZONE.UNVALID) {
        editor.selection.bindListener()
      }

      if (this.mousezone.mousedown === ZONE.EDITABLE) {
        // 鼠标在可编辑区中
        this.mousepointer = ZONE.EDITABLE

        // 添加 mouseenter 和 mouseleave 监听
        editable.addEventListener('mouseenter', mouseenter)
        editable.addEventListener('mouseleave', mouseleave)
      } else {
        // 鼠标不在可编辑区中
        this.mousepointer = ZONE.UNVALID
      }

      // 隐藏 toolbar 和 tooltip
      if ([ZONE.ASIDE, ZONE.EDITABLE].includes(this.mousezone.mousedown)) {
        editor.$moniter.$emit('hidebar', null).$emit('hidetip', null)
      }
    }

    // 鼠标弹起事件的回调函数
    const mouseup = (e: MouseEvent) => {
      // 解除选区的 selectionchange 监听
      editor.selection.unbindListener()

      // editor.$moniter.$emit('menuActive', null)

      this.transform(e)

      editor.input.focus()

      // 解除 mouseenter 和 mouseleave 监听
      if (this.mousezone.mousedown === ZONE.EDITABLE) {
        editable.removeEventListener('mouseenter', mouseenter)
        editable.removeEventListener('mouseleave', mouseleave)
      }
    }

    editor.$moniter
      .$on('mounted', () => {
        document.addEventListener('mousedown', mousedown)
        document.addEventListener('mouseup', mouseup)
      })
      .$on('unmounted', () => {
        document.removeEventListener('mousedown', mousedown)
        document.removeEventListener('mouseup', mouseup)
      })
  }

  /**
   * 用于记录 mousemove 过程中鼠标是否处于可编辑区中
   */
  public mousepointer: ZONE.UNVALID | ZONE.EDITABLE = ZONE.UNVALID

  /**
   * 鼠标在那个区域内
   */
  public mousezone: MouseZone = {
    mousedown: ZONE.UNVALID,
    mouseup: ZONE.UNVALID,
  }

  /**
   * 鼠标所在区域不是无效的
   * @returns
   */
  public zoneNotUnvalid() {
    return this.mousezone.mousedown !== ZONE.UNVALID && this.mousezone.mouseup !== ZONE.UNVALID
  }

  /**
   * 鼠标所在区域不是未定义的
   * @returns
   */
  public zoneNotUnkonwn() {
    return this.mousezone.mousedown !== ZONE.UNKNOWN && this.mousezone.mouseup !== ZONE.UNKNOWN
  }

  /**
   * 鼠标所在区域在可编辑区中
   * @returns
   */
  public zoneIsEditable() {
    return (
      this.mousezone.mousedown === ZONE.EDITABLE &&
      ((this.mouseelement.mouseup && this.mousezone.mouseup === ZONE.EDITABLE) || this.mousepointer === ZONE.EDITABLE)
    )
  }

  /**
   * 触发 MouseEvent 的元素
   */
  public mouseelement: MouseElement = {
    mousedown: null,
    mouseup: null,
  }

  /**
   * MouseEvent 事件的触发路径
   */
  public mousepath: MousePath = {
    mousedown: null,
    mouseup: null,
  }

  /**
   * 鼠标按下和鼠标弹起由同一个元素触发
   * @returns
   */
  public collapsed() {
    return this.mouseelement.mousedown !== null && this.mouseelement.mousedown === this.mouseelement.mouseup
  }

  /**
   * 是否是在 block 元素上触发
   */
  public mouseblock: MouseBlock = {
    mousedown: false,
    mouseup: false,
  }

  /**
   * 解析 MouseEvent 并对解析结果进行缓存
   * @param e MouseEvent
   */
  public transform(e: MouseEvent) {
    if (MOUSETYPE.includes(e.type)) {
      const type = e.type as MouseType

      this.clear(type)
      if (type === 'mousedown') {
        this.clear('mouseup')
      }

      const $refs = this.editor.$refs
      const path = e.composedPath()

      let zone = false
      const stack: EventTarget[] = []

      //
      for (let i = 0; i < path.length; i++) {
        const elem = path[i] as HTMLElement

        // if (NODE_NAME_REGEXP.test(Reflect.get(elem, 'nodeName')) && Reflect.get(elem, 'blockLabel')) {
        if (elem.hasAttribute(this.editor.$data.blockLabel)) {
          stack.length = 0
          Reflect.set(this.mouseblock, type, true)
          stack.push(elem)
          continue
        }

        // 缓存该元素
        stack.push(elem)

        // 判断触发 MouseEvent 的元素所在区域
        if (!zone) {
          if (elem === $refs.toolbar) {
            Reflect.set(this.mousezone, type, ZONE.TOOLBAR)
            zone = true
            continue
          }
          if (elem === $refs.tooltip) {
            Reflect.set(this.mousezone, type, ZONE.TOOLTIP)
            zone = true
            continue
          }
          if (elem === $refs.editable) {
            Reflect.set(this.mousezone, type, ZONE.EDITABLE)
            zone = true
            continue
          }
          if (elem === $refs.aside) {
            Reflect.set(this.mousezone, type, ZONE.ASIDE)
            zone = true
            continue
          }
        }

        // 终止循环
        if (elem === $refs.root) {
          // MouseEvent 在未定义类型的区域中触发
          if (!zone) {
            Reflect.set(this.mousezone, type, ZONE.UNKNOWN)
            zone = true
          }

          // 缓存 MouseEvent 的触发路径
          Reflect.set(this.mousepath, type, stack)

          // 定义触发 MouseEvent 的元素
          Reflect.set(this.mouseelement, type, stack[0])
          break
        }
      }

      // 鼠标未在当前 shadowRoot 下触发（清除数据）
      if (!zone) {
        this.clear(type)
      }
    }
  }

  /**
   * 清除数据
   * @param type MouseEvent.type
   */
  public clear(type: MouseType) {
    Reflect.set(this.mousezone, type, ZONE.UNVALID)
    Reflect.set(this.mousepath, type, null)
    Reflect.set(this.mouseelement, type, null)
    Reflect.set(this.mouseblock, type, false)
  }
}
